package org.giovanolli.mumi.controller;

import java.util.List;
import java.util.Locale;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;

import org.giovanolli.mumi.service.Service;
import org.giovanolli.mumi.view.datamodel.ListDataModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.context.MessageSource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

public abstract class AbstractController<E> {

	@Autowired
	protected MessageSource messageSource;

	protected final static String CONFIRMATION_MESSAGE_KEY = "confirmationMessage";

	protected abstract void bindEntityToModel();

	protected abstract void bindModelToEntity();

	@Transactional(readOnly = true)
	public boolean confirmSave(MessageContext messageContext, Locale locale) {
		bindModelToEntity();
		Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
		Set<ConstraintViolation<E>> constraintViolations = validator.validate(getModel().getEditedEntity());
		if (constraintViolations != null && constraintViolations.size() > 0) {
			for (ConstraintViolation constraintViolation : constraintViolations) {
				messageContext.addMessage(new MessageBuilder().error().source(constraintViolation.getPropertyPath().toString().replace('.', '_'))
				        .defaultText(constraintViolation.getMessage()).build());
			}
			return false;
		}
		save();
		messageContext.addMessage(new MessageBuilder().info().defaultText(messageSource.getMessage(CONFIRMATION_MESSAGE_KEY, null, locale)).build());
		return true;
	}

	@Transactional(readOnly = true)
	public void delete() {
		performDelete();
	}

	protected abstract Class<E> getEntityClass();

	protected abstract AbstractModel<E> getModel();

	protected abstract Service<E> getService();

	public void init() {
		getModel().setMode(UseCaseMode.SEARCH);
	}

	private void performDelete() {
		getService().deleteEntity(getModel().getEditedEntity());
	}

	public void populateNonEntityPropertiesToModel() {

	}

	@Transactional(readOnly = true)
	public void prepareEntityRender(String uid, UseCaseMode useCaseMode) throws InstantiationException, IllegalAccessException {
		if (((useCaseMode.equals(UseCaseMode.EDIT) || useCaseMode.equals(UseCaseMode.CONSULT)) && !StringUtils.hasText(uid))
		        || (useCaseMode.equals(UseCaseMode.NEW) && StringUtils.hasText(uid)) || useCaseMode.equals(UseCaseMode.SEARCH)) {
			throw new IllegalStateException();
		}
		getModel().setMode(useCaseMode);
		switch (useCaseMode) {
		case NEW:
			getModel().setEditedEntity(getEntityClass().newInstance());
			break;
		case EDIT:
		case CONSULT:
			getModel().setEditedEntity(getService().findByUid(uid));
		}
		bindEntityToModel();
	}

	protected void save() {
		getService().saveEntity(getModel().getEditedEntity());
	}

	@Transactional(readOnly = true)
	public void search() {
		List<E> entityList = getService().getList(getModel().getFilter());
		getModel().setList(new ListDataModel<E>(entityList));
		getModel().setPageNumber(1);
	}

}
